package com.globalwave.common.db.gen;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Transient;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.annotations.GenericGenerator;

import com.globalwave.base.BaseEntity;
import com.globalwave.base.annotations.Comparison;


public class ObjectBeanSqlGenerator extends SQLGenerator {

	private Object bean ;
	
	public ObjectBeanSqlGenerator(Object bean) {
		this.bean = bean ;
	}
	

    
    public WHERE getWhere() throws Exception {
                
        WHERE where = new WHERE() ;
        
        Field[] descriptors = bean.getClass().getDeclaredFields();
        for (Field field : descriptors) {
            
            final String fieldName = field.getName() ;  
            
            // filter static, eg. SQL - String
            if (Modifier.isStatic(field.getModifiers())) {
                continue ;
            }
            
            
            if (!PropertyUtils.isReadable(bean,fieldName)) {
            	continue ;
            }
            
            final Object fieldValue = PropertyUtils.getProperty(bean,fieldName) ;
            
            if (fieldValue == null) {
                continue ;
            }
            
            if (where.sql.length() == 0) {
                where.sql.append(" WHERE ") ;
            } else {
                where.sql.append( " AND " ) ;
            }
                        
            Comparison comparison = field.getAnnotation(Comparison.class) ;
            Column column = field.getAnnotation(Column.class) ;
            
            String leftExp = null ;
            String rightExp = "?" ;
            String operator = null ;
            List<Object> params = null ;
            
            // default value setting
            if (column == null) {
                leftExp = fieldName ;
            } else {
                leftExp = column.name() ;
            }
            
            if (fieldValue instanceof String) {
                operator = Comparison.LIKE ;
                if (comparison != null && comparison.isCaseIgnore()) {
                    leftExp = toLower(leftExp) ;
                    rightExp = toLower(rightExp) ;
                }
            } else if (fieldValue instanceof Object[]) {
                operator = Comparison.IN ;
                Object[] values = ((Object[])fieldValue) ;
                params = new ArrayList<Object>(values.length) ;
                for (int i = 0 ; i < values.length ; i ++) {
                    params.add(values[i]) ;
                    if (i != 0) {
                        rightExp += ",?" ;                        
                    }
                }
                rightExp = "("+ rightExp + ")" ;
            } else {
                operator = "=" ;
            }

            if (comparison != null) {
                operator = comparison.operator() ;
            }

            where.sql.append(" " + leftExp + " " + operator) ;
            
            if (!Comparison.IS_NULL.equals(operator) && !Comparison.IS_NOT_NULL.equals(operator)) {
            	where.sql.append(" " + rightExp + " ") ;
            }
            
            if (params == null) {
            	if (Comparison.LIKE.equals(operator)) {
                    where.params.add("%" + fieldValue + "%") ;            		
            	} else if (Comparison.IS_NULL.equals(operator)
            			||Comparison.IS_NOT_NULL.equals(operator)) {
            		//where.params.add(fieldValue) ; // no params
            	} else {
                    where.params.add(fieldValue) ;	
            	}
            } else {
                where.params.addAll(params) ;
                params = null ;
            }
        }
        
        return where ;
    }
    
    
    

    public INSERT getInsert() throws Exception {
        String tableName = this.getTableName() ;
        String columnName = null ;
        
        INSERT create = new INSERT() ;
        
        StringBuffer insert = new StringBuffer() ;
        StringBuffer values = new StringBuffer() ;
        
        Field[] descriptors = bean.getClass().getDeclaredFields();
        for (Field field : descriptors) {
            
            Transient transn = field.getAnnotation(Transient.class) ;
            if (transn != null) {
                continue ;
            }

            if (Modifier.isStatic(field.getModifiers())) {
                continue ;
            }
            
            Object fieldValue = PropertyUtils.getProperty(bean, field.getName()) ;
            Id id = field.getAnnotation(Id.class) ;
            
            if (fieldValue == null && id == null) {
                continue ;
            }
            
            if (insert.length() == 0) {
                insert.append("INSERT INTO " + tableName + " (") ;
                values.append("VALUES(  ?") ;
            } else {
                insert.append(" , ") ;
                values.append(" , ?") ;
            }
                        
            Column column = field.getAnnotation(Column.class) ;
                        
            if (column != null) {
                columnName = column.name() ;
            } else {
                columnName = field.getName() ;
            }
            insert.append(columnName) ;
            
            if(id != null && fieldValue == null) {// id 未有值
            	GenericGenerator generator = field.getAnnotation(GenericGenerator.class) ;
            	
                Object idValue = null ;
            	if (generator == null) {
            		idValue = getId(id, tableName, columnName) ;
            	} else {
            		idValue = dao.getNextValue(generator.name()) ;
            	}
                if (idValue != null) {
                    fieldValue = idValue ;
                    BeanUtils.setProperty(bean, field.getName(),idValue) ;
                }
            }
            
            create.params.add(fieldValue) ;
        }
        
        insert.append(" ) ") ;
        values.append(" ) ") ;
        
        create.sql.append(insert).append(values) ;
                
        return create ;
    }
    
    
    

    
    public UPDATE getUpdate() throws Exception {

    	Set<String> exclusions = null ;
    	Set<String> inclusions = null ;
        
        if (bean instanceof BaseEntity) {
            exclusions = ((BaseEntity)bean).getExclusions() ;
        	inclusions = ((BaseEntity)bean).getInclusions() ;
        }
        
        UPDATE update = new UPDATE() ;
                
        Field[] descriptors = bean.getClass().getDeclaredFields();
        for (Field field : descriptors) {

            Transient transn = field.getAnnotation(Transient.class) ;
            if (transn != null) {
                continue ;
            }

            if (Modifier.isStatic(field.getModifiers())) {
                continue ;
            }
            
            if (field.getAnnotation(Id.class) != null) {
            	continue ;
            }
            
            final String fieldName = field.getName() ;
            
            if (exclusions != null && exclusions.contains(fieldName)) {
                continue ;
            }
            
            if (inclusions != null && !inclusions.contains(fieldName)) {
                continue ;
            }
            
            Object fieldValue = PropertyUtils.getProperty(bean,fieldName) ;
            
            if (update.sql.length() == 0) {
                update.sql.append("UPDATE " + this.getTableName() + " SET ") ;
            } else {
                update.sql.append(" , ") ;
            }
                        
            Column column = field.getAnnotation(Column.class) ;
                        
            if (column != null) {
                update.sql.append(column.name()) ;
            } else {
                update.sql.append(fieldName) ;
            }
            update.sql.append(" = ? ") ;
            
            update.params.add(fieldValue) ;
        }
        
        
        
        return update ;
    }

    public String getTableName() {
    	Class<?> clazz = this.bean.getClass() ;
    	return getTableName(clazz) ;
    }
    
    public WHERE getIdWhere() {
        WHERE where = null;
        
        Field[] descriptors = bean.getClass().getDeclaredFields();
        for (Field field : descriptors) {
            Id id = field.getAnnotation(Id.class) ;
            if (id != null) {
                try {
                	Object value = BeanUtils.getProperty(bean, field.getName()) ;
                	if (value == null) {
                		break ;
                	}
                	
                	if (where == null) {
                		where = new WHERE() ;
                        where.sql.append(" WHERE ") ;
                	}
                    where.params.add(value) ;
                } catch (Exception e) {
                	log.error(e, e) ;
                	break ;
                }            
                
                
                Column column = field.getAnnotation(Column.class) ;
                
                if (column != null) {
                    where.sql.append(column.name()) ;
                } else {
                    where.sql.append(field.getName()) ;
                }
                
                where.sql.append(" = ? ") ;
                break ;
            }
        }
        
        return where ;
    }
    
    public void setBean(Object bean) {
		this.bean = bean;
	}
}
